﻿package net.chrisjrock.ui
{
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.utils.Dictionary;
	
	/**
	 * ...
	 * @author Christopher J. Rock
	 */
	public class KeyboardTracker extends EventDispatcher
	{
		public static const ACTIVATE:String = "keyTrackActivate";
		public static const DEACTIVATE:String = "keyTrackDeactivate";
		
		private static var _initiating:Boolean;
		private static var _instance:KeyboardTracker;
		
		public static function initiateKeyboardTracker (stage:Stage , enabled:Boolean = true):void
		{
			_initiating = true;
			_instance = new KeyboardTracker(stage , enabled);
			_initiating = false;
		}
		
		public static function getInstance ():KeyboardTracker
		{
			return _instance;
		}
		
		private var _stage:Stage;
		private var _keyDown:Dictionary;
		private var _keyList:Vector.<uint>;
		private var _mouseDown:Boolean;
		
		private var _enabled:Boolean;
		
		public function KeyboardTracker (stage:Stage , enabled:Boolean = true)
		{
			if (!_initiating)
			{
				throw(new Error("Cannot not manually instantiate the KeyboardTracker. Use KeyboardTracker.initiateKeyboardTracker method."));
			}
			
			if (_instance)
			{
				throw(new Error("Cannot create more than one KeyboardTracker instance."));
			}
			
			_stage = stage;
			
			setEnabled(enabled);
		}
		
		public function destroyExternal ():void
		{
			removeStageListeners();
		}
		
		public function destroyInternal ():void
		{
			_stage = null;
			_keyDown = null;
			_keyList = null;
			_mouseDown = false;
			_enabled = false;
		}
		
		private function addStageListeners ():void
		{
			if (_stage)
			{
				_stage.addEventListener(KeyboardEvent.KEY_DOWN , onKeyDown);
				_stage.addEventListener(KeyboardEvent.KEY_UP , onKeyUp);
				_stage.addEventListener(MouseEvent.MOUSE_DOWN , onMouseDown);
				_stage.addEventListener(MouseEvent.MOUSE_UP , onMouseUp);
			}
		}
		
		private function removeStageListeners ():void
		{
			if (_stage)
			{
				_stage.removeEventListener(KeyboardEvent.KEY_DOWN , onKeyDown);
				_stage.removeEventListener(KeyboardEvent.KEY_UP , onKeyUp);
				_stage.removeEventListener(MouseEvent.MOUSE_DOWN , onMouseDown);
				_stage.removeEventListener(MouseEvent.MOUSE_UP , onMouseUp);
			}
		}
		
		private function onKeyDown (event:KeyboardEvent):void
		{
			if (!_keyDown[event.keyCode])
			{
				_keyDown[event.keyCode] = true;
				_keyList[_keyList.length] = event.keyCode;
				dispatchEvent(event);
			}
		}
		
		private function onKeyUp (event:KeyboardEvent):void
		{
			if (_keyDown[event.keyCode])
			{
				delete _keyDown[event.keyCode];
				_keyList.splice(_keyList.indexOf(event.keyCode) , 1);
				dispatchEvent(event);
			}
		}
		
		private function onMouseDown (event:MouseEvent):void
		{
			if (!_mouseDown)
			{
				_mouseDown = true;
				dispatchEvent(event);
			}
		}
		
		private function onMouseUp (event:MouseEvent):void
		{
			if (_mouseDown)
			{
				_mouseDown = false;
				dispatchEvent(event);
			}
		}
		
		public function isKeyDown (key:uint):Boolean
		{
			return _keyDown ? _keyDown[key] : false;
		}
		
		public function isKeyComboDown (keyCombo:Vector.<uint>):Boolean
		{
			var keyNum:int = keyCombo.length - 1;
			for (; keyNum >= 0 ; keyNum--)
			{
				if (!isKeyDown(keyCombo[keyNum]))
				{
					return false;
				}
			}
			
			return true;
		}
		
		public function getKeysDown ():Vector.<uint>
		{
			return _keyList ? _keyList.concat() : null;
		}
		
		public function isMouseDown ():Boolean
		{
			return _mouseDown;
		}
		
		public function isEnabled ():Boolean
		{
			return _enabled;
		}
		
		public function setEnabled (value:Boolean):void
		{
			if (_enabled != value)
			{
				_enabled = value;
				
				if (_enabled)
				{
					addStageListeners();
					_keyDown = new Dictionary();
					_keyList = new Vector.<uint>();
					_enabled = true;
					dispatchEvent(new Event(ACTIVATE));
				}
				else
				{
					removeStageListeners();
					_keyDown = null;
					_keyList = null;
					_mouseDown = false;
					_enabled = false;
					dispatchEvent(new Event(DEACTIVATE));
				}
			}
		}
	}
	
}